#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2020/4/23 0023 1:04
# @Author  : liangshuo
# @File    : views.py
# @Software : PyCharm
from rest_framework.response import Response
from rest_framework.views import APIView

from utils.response import APIResponse  # 二次封装response
from base.views import BaseViewSet, APIBaseViewSet
from .models import Book, Publish
from .serializers import BookSerializer, PublishSerializer, BookDModelSerializer, V2BookModelSerializer


# 100 序列化
class PublishViewSet(APIBaseViewSet):
    """publish"""

    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            try:
                publish_obj = Publish.objects.filter(is_deleted=False, pk=pk).first()
                publish_data = PublishSerializer(publish_obj).data
            except:
                return Response({'status': 1, 'msg': '数据不存在'})
        else:
            publish_query = Publish.objects.filter(is_deleted=False).all()
            publish_data = PublishSerializer(publish_query, many=True).data

        return Response({'status': 0, 'msg': '成功', 'result': publish_data})


# 200 序列化、反序列化
class BookViewSet(APIView):
    """book"""

    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        # 单查
        if pk:
            try:
                # 2个等价 但是get获取不到会报异常
                book_obj = Book.objects.get(pk=pk, is_deleted=False)
                # book_obj = Book.objects.filter(pk=pk, is_deleted=False).first()
                book_data = BookSerializer(book_obj).data  # 单条数据序列化
            except:
                return Response({
                    'status': 1,
                    'msg': '数据不存在'
                })
        # 群查
        else:
            book_query = Book.objects.filter(is_deleted=False).all()  # 先筛选查询,再查所有
            book_data = BookSerializer(book_query, many=True).data  # 不管是一条还是多条，只要数据是被[]嵌套，都要写many=True

        # 返回前端数据
        return Response({
            'status': 0,
            'msg': 'ok',
            'results': book_data
        })

    def post(self, request, *args, **kwargs):
        request_data = request.data  # post提交的数据
        book_ser = BookDModelSerializer(data=request_data)
        book_ser.is_valid(raise_exception=True)  # 检验失败，终止视图方法
        # raise_exception 自动抛出异常给前台. 类似于之前的serialier.errors
        book_obj = book_ser.save()

        return Response({'status': 0,
                         'msg': '成功',
                         'result': BookSerializer(book_obj).data})


# 300 整合（正反序列化）
class V2BookViewSet(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        # 单查
        if pk:
            try:
                # 2个等价 但是get获取不到会报异常
                book_obj = Book.objects.get(pk=pk, is_deleted=False)
                book_data = V2BookModelSerializer(book_obj).data  # 单条数据序列化
            except:
                return Response({
                    'status': 1,
                    'msg': '数据不存在'
                })
        # 群查
        else:
            book_query = Book.objects.filter(is_deleted=False).all()  # 先筛选查询,再查所有
            book_data = V2BookModelSerializer(book_query, many=True).data  # 不管是一条还是多条，只要数据是被[]嵌套，都要写many=True

        # 返回前端数据
        return Response({
            'status': 0,
            'msg': 'ok',
            'results': book_data
        })

    def post(self, request, *args, **kwargs):
        request_data = request.data  # post提交的数据
        book_ser = V2BookModelSerializer(data=request_data)
        book_ser.is_valid(raise_exception=True)  # 检验失败，终止视图方法
        # raise_exception 自动抛出异常给前台. 类似于之前的serialier.errors
        book_obj = book_ser.save()

        return Response({'status': 0,
                         'msg': '成功',
                         'result': V2BookModelSerializer(book_obj).data})


# 400 群查、群增、群删、群改
"""
总结
    # 序列化
        ser_obj = ModelSerializer(instance=model_obj)
        data=new_request_data, many=True, partial=True)
    # 反序列化 
        # save() ==> create()
        ser_obj = ModelSerializer(data=model_data)
    # save()==>update
        ser_obj = ModelSerializer(instance=model_obj,data=model_data)
        # partial=True ==> 匹配字段request=True校验条件  
    # 部分字段更新update
        ser_obj = ModelSerializer(instance=model_obj,data=model_data,partial=True )
"""


class V3BookViewSet(APIView):
    # 410
    # 单查：有pk
    # 群查：无pk
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        # 单查
        if pk:
            try:
                # 2个等价 但是get获取不到会报异常
                book_obj = Book.objects.get(pk=pk, is_deleted=False)
                book_data = V2BookModelSerializer(book_obj).data  # 单条数据序列化
            except:
                return Response({
                    'status': 1,
                    'msg': '数据不存在'
                })
        # 群查
        else:
            book_query = Book.objects.filter(is_deleted=False).all()  # 先筛选查询,再查所有
            book_data = V2BookModelSerializer(book_query, many=True).data  # 不管是一条还是多条，只要数据是被[]嵌套，都要写many=True

        # 返回前端数据
        return Response({
            'status': 0,
            'msg': 'ok',
            'results': book_data
        })

    # 420
    # v1版本：容易理解
    # 单增对应的是，many=False
    # 群增对应的是，many=True
    def v1post(self, request, *args, **kwargs):
        request_data = request.data  # post提交的数据
        print(type(request_data))

        # 单增对应的是，many=False
        if isinstance(request_data, dict):
            book_ser = V2BookModelSerializer(data=request_data, many=False)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()

            return Response({'status': 0,
                             'msg': '成功',
                             'result': V2BookModelSerializer(book_obj, many=False).data})

        # 群增对应的是，many=True
        elif isinstance(request_data, list):
            book_ser = V2BookModelSerializer(data=request_data, many=True)  # many=Ture
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()

            return Response({'status': 0,
                             'msg': '成功',
                             'result': V2BookModelSerializer(book_obj, many=True).data})  # many=Ture

    # 单增 {}：传的数据是与model对应的字典  many=False
    # 群增 [{},{},{}] ：传的数据是 装多个model对应字典的列表， many=True
    # 为什么可以群增？ModelSerial里面重写了create方法
    def post(self, request, *args, **kwargs):
        """
        [
            {
                "name": "天才与疯子3",
                "price": "66.00",
                "publish": 1,
                "authors":[3]
            },
            {
                "name": "天才与疯子4",
                "price": "66.00",
                "publish": 2,
                "authors":[2]
            }
        ]
        """
        request_data = request.data  # post提交的数据
        print(type(request_data))

        # 单增对应的是，many=False
        many = False
        if isinstance(request_data, dict):
            pass
        # 群增对应的是，many=True
        elif isinstance(request_data, list):
            many = True
        else:
            return Response({'status': 1, 'msg': '数据格式有误'}, status=404)

        book_ser = V2BookModelSerializer(data=request_data, many=many)
        book_ser.is_valid(raise_exception=True)
        book_obj = book_ser.save()

        return Response({'status': 0,
                         'msg': '成功',
                         'result': V2BookModelSerializer(book_obj, many=many).data})

    # 430
    # 单删： 有pk
    # 群删：有pks  {"pks":[1,2,3]}
    def delete(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            pks = [pk]
        else:
            pks = request.data.get('pks')

        update_rows = Book.objects.filter(is_deleted=False, pk__in=pks).update(is_deleted=True)
        if update_rows:
            return Response({'status': 0,
                             'msg': '删除成功'})
        else:
            return Response({'status': 1,
                             'msg': '删除失败'})

    # 440
    # 单整体修改：对 v3book/(pk)/ 传的数据 是与model对应的字典 name|price|publish|authors
    # partial=False
    def put(self, request, *args, **kwargs):

        # 01、获取要更新的对象instance和要更新data
        request_data = request.data
        pk = kwargs.get('pk')
        old_book_obj = Book.objects.filter(pk=pk, is_deleted=False).first()

        # 02、序列化类（此时扮演验证器）：校验，通过的插入数据库，不通过的返回
        # 方法1：可以直接update： 但是不经过校验，数据库插入很多无用的数据，会崩溃
        # 方法2：目的：将众多数据的校验交给序列化类来处理
        # 让序列化类来扮演反序列化角色，当校验成功后，让序列化类来帮你入库
        book_ser = V2BookModelSerializer(instance=old_book_obj, data=request_data,
                                         partial=False)  # BaseSerializer的init方法
        book_ser.is_valid(raise_exception=True)

        # 03、校验通过：完成数据的更新（要更新的目标，用来更新的新数据）
        # 对象instance存在，update
        # 不存在，则create
        book_obj = book_ser.save()

        # 04、序列化（已经更新的对象instance），并返回数据
        return Response({'status': 0, 'msg': '成功', 'result': V2BookModelSerializer(instance=book_obj).data})

    # 单局部修改：v3book/(pk)/ 传的数据，数据字段key都是选填
    # partial=True
    def patchv1(self, request, *args, **kwargs):
        request_data = request.data
        pk = kwargs.get('pk')
        old_book_obj = Book.objects.filter(pk=pk, is_deleted=False).first()

        book_ser = V2BookModelSerializer(instance=old_book_obj, data=request_data, partial=True)
        # partial=True 使require=True 校验规则失效
        book_ser.is_valid(raise_exception=True)
        book_obj = book_ser.save()

        return Response({'status': 0, 'msg': '成功', 'result': V2BookModelSerializer(instance=book_obj).data})

    # 总结
    """
    1) 单整体修改
    V2BookModelSerializer(
        instance=old_book_obj,  要被更新的对象
        data=request_data, 用来更新的数据
        partial=False  默认为False，必须的字段全部参与校验
        )

    2) 单局部更改：
    V2BookModelSerializer(
        instance=old_book_obj,  要被更新的对象
        data=request_data, 用来更新的数据
        partial=False  设置为True，必须的字段变为选填字段
        )

    注：partial设置True的本质就是使字段require=True 校验规则失效
    """

    # 450
    # 群局部改
    # 请求数据 - [{pk:1,name:'lalal'},{},{}]  错误示范{1:{},2:{}}
    def patch(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        request_data = request.data

        # 01、将单改、群改的数据都格式化成
        # pks=[要修改的对象主键标识,] request_data = [每个要修改的对象对应的修改数据,]
        if pk and isinstance(request_data, dict):  # 单改
            pks = [pk, ]
            request_data = [request_data, ]
        elif not pk and isinstance(request_data, list):  # 群改
            pks = []
            for dic in request_data:  # 遍历前台数据[pk:1, name:123], 取出正确的数据
                pk = dic.pop('pk', None)
                if pk:
                    pks.append(pk)
                else:
                    return Response({'status': 1, 'msg': '数据有误'})
        else:
            return Response({'status': 1, 'msg': '数据有误'})

        # 02、pks与request——data数据筛选
        # 1) 将pks中没有对应的数据pk与数据已删除的pk移除，request_data对应索引位上的数据也移除
        # 2) 将合理的pks转化为objs
        print(pks)  # [14, 13]
        print(request_data)  # [{'name': '天才与疯子004'}, {'name': '天才与疯子004'}]
        objs = []
        new_request_data = []
        for index, pk in enumerate(pks):
            try:
                # pk对应的数据合理，将合理的对象存储-数据得存在且没有删除
                obj = Book.objects.get(pk=pk, is_deleted=False)
                objs.append(obj)
                # 对应索引的数据需要保存下来
                new_request_data.append(request_data[index])
            except:
                continue
                # ERROR: 永远不要在for中对数据的index进行操作
                # 重点：-反面教材：pk对应的数据有误，将对应索引的data中request_data中移除
                # index = pks.index(pk)
                # request_data.pop(index)

        # 需求：
        # 1) 在视图类中，可以通过request得到登录用户request.use
        # 2) 在序列化类中，要完成数据库数据的校验与入库操作，可能会需要知道当前的登录用户，但序列化类无法访问request
        # 3) 在视图类中实例化序列化对象是，将request对象传递进去
        # book_ser = V2BookModelSerializer(instance=objs, data=new_request_data, many=True, partial=True)
        book_ser = V2BookModelSerializer(instance=objs, data=new_request_data, many=True, partial=True,
                                         context={'request': request})  # 把request从view传给Serializer
        book_ser.is_valid(raise_exception=True)
        book_ser.save()  # update
        # 一旦设置了many=True,反序列化情况下的create、update就不再调用ModelSerializer的
        # 而是调用ModelSerialzier.Meta.list_serializer_class指向的ListSerializer类的create、update
        # ListSerializer默认只实现了群增的create，要实现群改，必须重写update
        """
        可以群增，但是不能群改
        ListSerializer  
            里面有create 可以实现群增
            有update，但是没有处理，只有异常
        Serializers with many=True do not support multiple update by default, only multiple create. 
        For updates it is unclear how to deal with insertions and deletions. 
        If you need to support multiple update, 
        use a `ListSerializer` class and override `.update()` so you can specify the behavior exactly.
        """
        # return Response({'status': 0, 'msg': '成功', 'result': V2BookModelSerializer(instance=objs, many=True).data})

        # 04、采用自定义二次封装response（返回json格式）的响应对象
        data = V2BookModelSerializer(instance=objs, many=True).data
        return APIResponse(data_status=0, data_msg='成功',
                           token='123.abc.xyz',
                           results=data, http_status=201)
